#region License
// /* **************************************************************
// * Copyright (C) intex Consulting GmbH - All Rights Reserved    *
// *--------------------------------------------------------------*
// * Unauthorized copying of this file, via any medium is         *
// * strictly prohibited                                          *
// *--------------------------------------------------------------*
// * Proprietary and confidential                                 *
// *--------------------------------------------------------------*
// * Dissasembling/modifications strictly prohibited!             *
// ***************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using ComHlp.ContentClasses;
using ComHlp.ContentClasses.FormDef;
using ComHlp.ContentClasses.VirtualControls;
using ComHlp.ContentClasses.VirtualControls.Buttons;
using ComHlp.ContentClasses.VirtualControls.LView;
using ComHlp.enums.Fields;
using ComHlp.Exceptions;

namespace ComHlp.Classes.Parser
{
    internal class FormDefParser
    {
        /// <summary> Add given map to cache (parsing given received string) </summary>
        /// <param name="recvdForm"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"><paramref name="recvdForm"/> is <see langword="null" />.</exception>
        /// <exception cref="CommunicationException">Communication error: No window title received</exception>
        internal void AddMapToCache(string recvdForm)
        {
            if (recvdForm == null) throw new ArgumentNullException(nameof(recvdForm));

            StringBuilder sbRecvStr = new StringBuilder(recvdForm);
            ItxListViewContainer itxListViewContainer = null;

            // formdef header
            FormDefHeader formDefHeader = FormDefHelper.SplitHeader(ref sbRecvStr);

            // constants
            StringBuilder sbConstantsString = new StringBuilder(FormDefHelper.SplitConstants(ref sbRecvStr));

            // field definitions
            StructFieldDefinitions structFieldDef = FormDefHelper.SplitFieldDef(ref sbRecvStr, formDefHeader.MaximumFieldLength, formDefHeader.TabControlTabCount);

            // constraints
            List<ItxFieldConstraint> fieldConstraints = FormDefHelper.SplitConstraints(structFieldDef.ConstraintsCount, ref sbRecvStr);

            // positions
            List<Point> fieldPositions = FormDefHelper.SplitPositions(ref sbRecvStr, formDefHeader.LastRow, formDefHeader.LastColumn, formDefHeader.MapFieldCount);

            // fieldadditions
            StructFieldAdditions fieldAdditions = FormDefHelper.SplitAdditions(ref sbRecvStr, formDefHeader.LastRow, formDefHeader.LastColumn, formDefHeader.MaximumConstLength, formDefHeader.ProtocolRevision);

            // windowtitle
            if (sbRecvStr[0] != 'B')
                throw new CommunicationException("Communication error: No window title received");
            formDefHeader.Title = sbRecvStr.ToString(3, sbRecvStr.Length - 3).TrimEnd();

            // add constants to fieldadditions
            AssignConstants(sbConstantsString, fieldAdditions);

            // have we got multiple listviews on the map?
            bool multipleListViews = fieldConstraints.Any(x => x.Undefined.StartsWith("AT"));

            /*
             * add constraints to fields and check for listview
             */
            // how often to repeat fields
            int repeationCount = 0;
            bool vmFound = false;
            ItxField previousField = null;
            List<ItxField> listFields = new List<ItxField>();
            List<ItxField> listRepeationFields = new List<ItxField>();
            foreach (ItxField itxField in structFieldDef.Fields)
            {
                // temp save old fieldtype
                ItxFieldType tmpFieldType = itxField.FieldType;

                /*
                 * field constraints  
                 */
                AssignConstraintsToField(itxField, fieldConstraints);

                /*
                 * listview handling
                 */
                if (itxField.FormDefName.StartsWith(ItxControlConstants.TableStart)) // indicates listview start
                {
                    formDefHeader.InternalListViewCount++;
                    itxField.FieldType = ItxFieldType.ListViewHeader;

                    /* 
                     * check for multiple listviews
                     */
                    if (multipleListViews)
                    {
                        int lvNo = FormDefHelper.ExtractListViewNoFromConstraints(itxField.FieldConstraints);
                        if (lvNo != -1)
                        {
                            itxField.ListViewNo = lvNo;

                            // have we allready got a listview?
                            if (itxListViewContainer == null)
                                itxListViewContainer = new ItxListViewContainer();

                            ItxListView fdLv = itxListViewContainer.ListViews.Find(tmpFdLv => tmpFdLv.ListViewNo == lvNo);
                            if (fdLv == null)
                            {
                                fdLv = new ItxListView(itxField.VirtualFieldNumber) { ListViewNo = lvNo };
                                itxListViewContainer.ListViews.Add(fdLv);
                            }
                        }
                    }
                    else
                    {   // we got only a single listview
                        if (itxListViewContainer == null)
                            itxListViewContainer = new ItxListViewContainer();

                        if (itxListViewContainer.ListViews.Count == 0)
                        {
                            ItxListView fdLv = new ItxListView(itxField.VirtualFieldNumber);
                            itxListViewContainer.ListViews.Add(fdLv);
                        }
                    }
                }
                else if (formDefHeader.InternalListViewCount > 0             // we need to check if it is repeationstart or end cause this is
                    && itxField.FieldType != ItxFieldType.RepeationStart // sending us an empty field which would set continuedListView variable to false
                    && itxField.FieldType != ItxFieldType.RepeationEnd)
                {
                    // listview vm
                    if (itxField.FormDefName.StartsWith(ItxControlConstants.VmName))
                    {
                        vmFound = true;
                        itxField.FieldType = ItxFieldType.ListViewVm;
                        itxField.ListViewNo = multipleListViews ? FormDefHelper.ExtractListViewNoFromConstraints(itxField.FieldConstraints) : 1;

                        // check for cp02 listviews where we receive the fields already expanded...
                        if (itxField.FormDefName.Length >= 4)
                        {
                            /*ok we just try parsing here... to get the row no - the only thing missing
                            2AVPEINH  003000000
                            5CTBU     097011312 5CTBU3    020011312
                            3CVM01    002020000 5CPOS01   097011312
                            3CVM301   001010000 5CPOS301  020011312
                            3CVM02    002020000 5CPOS02   097011312
                            3CVM302   001010000 5CPOS302  020011312
                            3CVM03    002020000 5CPOS03   097011312
                            3CVM303   001010000 5CPOS303  020011312
                            3CVM04    002020000 5CPOS04   097011312
                            3CVM304   001010000 5CPOS304  020011312
                            3CVM05    002020000 5CPOS05   097011312
                            3CVM305   001010000 5CPOS305  020011312
                            3CVM06    002020000 5CPOS06   097011312
                            3CVM07    002020000 5CPOS07   097011312
                            5CTBU4    020011312 3CVM08    002020000
                            5CPOS08   097011312 3CVM401   001010000
                            5CPOS401  020011312 3CVM09    002020000
                            5CPOS09   097011312 3CVM402   001010000
                            5CPOS402  020011312 3CVM10    002020000
                            5CPOS10   097011312 3CVM403   001010000*/
                            switch (itxField.FormDefName.Length)
                            {
                                case 4://VM01 - rowno at the end of the field
                                    itxField.ListViewRowNumber = ParseHelper.String2Int(itxField.FormDefName.Substring(2)) - 1;
                                    break;
                                case 5://VM301
                                    itxField.ListViewRowNumber = ParseHelper.String2Int(itxField.FormDefName.Substring(3)) - 1;
                                    break;
                            }
                        }
                    }
                    //20160607 ds fix due to cp02
                    else if (itxField.FormDefName == ItxControlConstants.TablePos
                        || (itxField.FormDefName.StartsWith(ItxControlConstants.TablePos) && itxField.FormDefName.Length > 3 && char.IsNumber(itxField.FormDefName, 3)))
                    {
                        // check listview position through constraints 
                        // this is row1, other rows will be counted up/added when expanding all fields
                        int lvNo = FormDefHelper.ExtractListViewNoFromConstraints(itxField.FieldConstraints);
                        //if (fdField.Length > 24 && lvNo > 0)
                        if (multipleListViews)
                        {
                            itxField.FieldType = ItxFieldType.ListViewElement;
                            itxField.ListViewNo = lvNo;

                            if (itxListViewContainer != null)
                            {
                                ItxListView fdLv = itxListViewContainer.ListViews.Find(tmpFdLv => tmpFdLv.ListViewNo == lvNo);
                                if (fdLv != null)
                                    fdLv.RowCount++;
                                else
                                {
                                    fdLv = new ItxListView(itxField.VirtualFieldNumber);
                                    fdLv.RowCount++;
                                    fdLv.ListViewNo = lvNo;
                                    itxListViewContainer.ListViews.Add(fdLv);
                                }
                            }
                        }
                        /*
                         * check listview (POS directly after TBU -> ListView)
                         *  we reach here for single listviews
                         */
                        else
                        {
                            // set id
                            itxField.ListViewNo = 1;
                            itxField.FieldType = ItxFieldType.ListViewElement;

                            if (itxListViewContainer?.ListViews.Count == 1)
                                itxListViewContainer.ListViews[0].RowCount++;
                            else
                            {
                                ItxListView newListView = new ItxListView(itxField.VirtualFieldNumber);
                                newListView.RowCount++;

                                // should never happen. but for savety we catch the null reference here
                                if (itxListViewContainer == null)
                                    itxListViewContainer = new ItxListViewContainer();

                                itxListViewContainer.ListViews.Add(newListView);
                            }
                        }
                    }
                    /* ok, we got 2 vm's - we receive table start, vm, <custom field we handle here>, pos
                        let us think about handling....
                    */
                    else if (itxField.FieldType == ItxFieldType.ListViewAddition)
                    {
                        
                        // only if we have got some repeation / are inside one
                        if (repeationCount > 0)
                        {
                            itxField.ListViewNo = multipleListViews ? FormDefHelper.ExtractListViewNoFromConstraints(itxField.FieldConstraints) : 1;

                            // check for 2cond vm
                            if (vmFound && previousField.FieldType == ItxFieldType.ListViewVm)
                                itxField.FieldType = ItxFieldType.ListViewVm2;
                            // check for listview addion - if parsing is correct
                            //else if (previousField?.FieldType == ItxFieldType.ListViewElement
                            //         || previousField?.FieldType == ItxFieldType.ListViewAddition)
                            //{
                                
                            //}
                        }
                    }

                    previousField = itxField;
                }

                /*
                 * handle repeation (collecting fields which belong to and expanding afterwards)
                 */
                switch (tmpFieldType)
                {
                    case ItxFieldType.RepeationStart:// repeation starts, the count of repeations is passed as field length
                        repeationCount = itxField.Length;
                        break;

                    case ItxFieldType.RepeationEnd:
                        if (repeationCount > 0)
                        {
                            // expand fields at end of repeation - also parsing into listview container
                            //  adding the listview no to the fields
                            var expandedFields = FormDefHelper.ExpandRepeationFields(repeationCount, listRepeationFields, itxListViewContainer);
                            listFields.AddRange(expandedFields);

                            // reset 
                            repeationCount = 0;
                            listRepeationFields.Clear();
                        }
                        break;

                    case ItxFieldType.RepeationElement:
                        listRepeationFields.Add(itxField);
                        break;

                    default:
                        listFields.Add(itxField);
                        break;
                }
            }

            /* positions 
                add field numeration/positions/additions 
                201604 parse status message */
            int fieldNo = 1;
            foreach (ItxField fdField in listFields)
            {
                // field position
                fdField.FormDefColumnNo = (int)fieldPositions[0].X;
                fdField.FormDefRowNo = (int)fieldPositions[0].Y;
                fieldPositions.RemoveAt(0);

                // field number (virtual)
                fdField.VirtualFieldNumber = fieldNo;

                // field additions (n addtitions : 1 field relation)
                List<ItxFieldAddition> fdAddSrch = fieldAdditions.ListFieldAdd.FindAll(tmpFdAdd => tmpFdAdd.FieldNo == fieldNo);
                if (fdAddSrch.Count > 0)
                {
                    int i = 0;
                    foreach (ItxFieldAddition fdAdd in fdAddSrch)
                    {
                        fdAdd.VirtualName = $"{fdField.FormDefName}{i++}";
                        fdField.FieldAdditions.Add(fdAdd);
                    }
                }

                // FKT counter, for special field on the upper right side
                if (fdField.FormDefName == ItxControlConstants.FormDefFunctionName)
                    formDefHeader.InternalFktFieldCount++;

                fieldNo++;
            }

            // find fc or fcsonder row no
            var fcField = listFields.FirstOrDefault(x => x.FormDefName == ItxControlConstants.FormDefFcName || x.FormDefName == ItxControlConstants.FormDefFcSonderName);
            if (fcField != null)
                formDefHeader.InternalFcRowNo = fcField.FormDefRowNo;

            /* we still need to do some special handling for the vm field
             * - set the listview row no/column no/field length/fielderror for the vm
             * - set the listview base properties regarding vm
             */
            if (itxListViewContainer != null && itxListViewContainer.ListViews.Count > 0)
            {
                ItxField lastVm = null;
                ItxField last2Vm = null;
                foreach (ItxField fdField in listFields)
                {
                    // listview switched?
                    if (lastVm != null && lastVm.ListViewNo != fdField.ListViewNo)
                    {
                        lastVm = null;
                        last2Vm = null;
                    }

                    if (fdField.FieldType == ItxFieldType.ListViewVm)
                        lastVm = fdField;

                    if (fdField.FieldType == ItxFieldType.ListViewVm2)
                        last2Vm = fdField;

                    // only elements are interesting
                    if (fdField.FieldType != ItxFieldType.ListViewElement)
                        continue;

                    // have we got already a last vm?
                    if (lastVm != null)
                    {
                        //// only transfer rownumber if we have got a valid rownumber
                        //if (lastVm.ListViewRowNumber == -1)
                        //    lastVm.ListViewRowNumber = fdField.ListViewRowNumber;

                        // get corrosponding listview
                        var listView = itxListViewContainer.ListViews.FirstOrDefault(x => x.ListViewNo == fdField.ListViewNo);
                        if (listView == null)
                            continue;

                        if (!listView.HasVmAssigned)
                        {
                            listView.HasVmAssigned = true;
                            listView.VmColumnNo = lastVm.FormDefColumnNo;
                            listView.VmFieldLength = lastVm.Length;
                        }

                        //if (lastVm.BsAttribute == ItxFieldState.Errenous)
                        //    listView.VmErroneousRow = lastVm.ListViewRowNumber;
                    }

                    // check for 2cond vm
                    if (last2Vm == null)
                        continue;

                    //// only transfer rownumber if we have got a valid rownumber
                    //if (last2Vm.ListViewRowNumber == -1)
                    //    last2Vm.ListViewRowNumber = fdField.ListViewRowNumber;

                    // get corrosponding listview
                    var listView2 = itxListViewContainer.ListViews.FirstOrDefault(x => x.ListViewNo == fdField.ListViewNo);
                    if (listView2 == null)
                        continue;

                    if (!listView2.Has2CondVmAssigned)
                    {
                        listView2.Has2CondVmAssigned = true;
                        listView2.Vm2ColumnNo = last2Vm.FormDefColumnNo;
                        listView2.Vm2FieldLength = last2Vm.Length;
                        listView2.Vm2Name = last2Vm.FormDefName;
                    }

                    //if (last2Vm.BsAttribute == ItxFieldState.Errenous)
                    //    listView2.Vm2ErroneousRow = last2Vm.ListViewRowNumber;
                }

                // any listview additions?
                if (listFields.Any(x => x.FieldType == ItxFieldType.ListViewAddition))
                {
                    foreach (var field in listFields.Where(x => x.FieldType == ItxFieldType.ListViewAddition))
                    {
                        // get listview no to which they belong
                        int listviewNo = FormDefHelper.ExtractListViewNoFromConstraints(field.FieldConstraints);
                        if (listviewNo < 1)
                            continue;

                        // get listview
                        var listView = itxListViewContainer.ListViews.FirstOrDefault(x => x.ListViewNo == listviewNo);
                        if(listView == null)
                            continue;
                        
                        // set last column in listview for expanding the listview to the right
                        if (listView.MostLastColumn == -1 || listView.MostLastColumn < field.FormDefColumnNo + field.Length)
                            listView.MostLastColumn = field.FormDefColumnNo + field.Length;

                        listView.HasAdditionalFields = true;
                    }
                }
            }

            // build final struct
            FormDefMap sfd = new FormDefMap
                {
                    Header = formDefHeader,
                    Fields = listFields,
                    UnassignedAdditions = fieldAdditions.ListFieldAdd.FindAll(tmpFdAdd => tmpFdAdd.FieldNo == -1),
                    RectangleHeadlines = fieldAdditions.ListFieldAdd.FindAll(tmpFdAdd => tmpFdAdd.FieldNo == -2),
                    Rectangles = fieldAdditions.ListRectangles,
                    ListViewCont = itxListViewContainer
                };

            // add to hashtable formcache and return
            FormDefCache.AddMapToCached(sfd);
        }

        private void AssignConstants(StringBuilder sbConstantsString, StructFieldAdditions fieldAdditions)
        {
            foreach (ItxFieldAddition itxFieldAddition in fieldAdditions.ListFieldAdd)
            {
                if (ComStatics.UseUtf8)
                    itxFieldAddition.Constant = ParseHelper.ExtractUtf8(itxFieldAddition.ConstLength, sbConstantsString, true);
                else
                {
                    itxFieldAddition.Constant = sbConstantsString.ToString(0, itxFieldAddition.ConstLength);
                    sbConstantsString.Remove(0, itxFieldAddition.ConstLength);
                }
            }
        }

        private void AssignConstraintsToField(ItxField itxField, List<ItxFieldConstraint> listfdConstraints)
        {
            for (int j = 0; j < itxField.ConstraintCount; j++)
            {
                var constraint = listfdConstraints[0];
                itxField.FieldConstraints.Add(constraint);

                // transfer button type
                switch (constraint.ButtonType)
                {
                    case EItxButtonType.YesCheckBox:
                    case EItxButtonType.NoCheckBox:
                    case EItxButtonType.YesNoCheckBox:
                    case EItxButtonType.ImmedPrintButton:
                    case EItxButtonType.MarkPrintButton:
                    case EItxButtonType.TextButton:
                    case EItxButtonType.OkButton:
                        itxField.FieldType = ItxFieldType.Button;
                        break;
                    //case ItxButtonType.NotSet:
                    //    break;
                }

                // check if it belongs to listview
                if (itxField.FieldType == ItxFieldType.Normal || itxField.FieldType == ItxFieldType.RepeationElement)
                {
                    // search after listview mark in constraints
                    //ItxFieldConstraint fdConst = listfdConstraints.Find(x => x.Undefined.StartsWith("AT"));
                    ItxFieldConstraint fdConst = itxField.FieldConstraints.Find(x => x.Undefined.StartsWith("AT"));
                    if (fdConst != null)
                        itxField.FieldType = ItxFieldType.ListViewAddition;
                }

                // if field is still Normal, check if it is a datefield
                if (itxField.FieldType == ItxFieldType.Normal)
                {
                    if(constraint.ReceivedType == 'D')
                        itxField.IsDatefield = true;
                }

                // remove constraint from list
                listfdConstraints.RemoveAt(0);
            }
        }
    }
}